//
// Created by martin on 8/2/21.
//

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/sysinfo.h>

#include "socket.h"
#include "error.h"
#include "strex.h"
#include "user.h"
#include "config.h"

#include <arpa/inet.h>
#include <linux/in6.h>


static Tunable_Params_t tunableParams;

static int config_parse_file(const char *filepath);
static int parse_line(char *s);


static const ParseConf_Params_str parseconf_params_array_str[] = {
        {"ip", tunableParams.ip},
        {"root_dir", tunableParams.root_dir},
        {NULL, NULL}
};

static const ParseConf_Params_bool parseconf_params_array_bool[] = {
        {"PASV_mode_enable", &tunableParams.PASV_mode_enable},
        {"PORT_mode_enable", &tunableParams.PORT_mode_enable},
        {NULL, NULL}
};

static const ParseConf_Params_int parseconf_params_array_int[] = {
        {"number_of_thread_max", &tunableParams.number_of_thread_max},
        {"number_of_client_max", &tunableParams.number_of_client_max},
        {"ctrl_conn_time_out", &tunableParams.ctrl_conn_time_out},
        {"data_conn_time_out", &tunableParams.data_conn_time_out},
        {NULL, NULL}
};

static const ParseConf_Params_uint parseconf_params_array_uint[] = {
        {"ctrl_port", &tunableParams.ctrl_port},
        {"data_port_PORT", &tunableParams.data_port_PORT},
        {"data_port_PASV_min", &tunableParams.data_port_PASV_min},
        {"data_port_PASV_max", &tunableParams.data_port_PASV_max},
        {NULL, NULL}
};

int config_init()
{
    config_parse_file(CONFIG_FILE_NAME);

    /* IO密集型，线程数为2×Ncpu为宜 */
    int core_num = get_nprocs_conf();
    if (tunableParams.number_of_thread_max <= 0 || tunableParams.number_of_thread_max > 4 * core_num) { /* number_of_thread_max异常 */
        tunableParams.number_of_thread_max = 2 * core_num;
    }

#if 0
    if (tunableParams.ctrl_port < 1024 || tunableParams.data_port_PORT < 1024) {
        if (user_getRootPrivilege() == -1) {
            err_sys("usage: app starts need root privilege when use well-known port");
        }
    }
#endif

    print_tunableParams();

    return 0;
}

static bool config_string_to_bool(char *s)
{
    if (strcasecmp(s, "yes") == 0 ||
    strcasecmp(s, "y") == 0 ||
    strcasecmp(s, "true") == 0 ||
    strcmp(s, "1") == 0) {
        return true;
    }

    return false;
}

static int parse_line(char *s)
{
    if (s == NULL) return -1;
    char name[100];
    char value[100];
    str_split(s, name, value, "=");
    str_trim_self(name);
    str_trim_self(value);

    if (strlen(name) == 0) { /* 配置参数名称为空 */
        err_sys("missing value in config file for line: %s", s);
    }

    /* 处理类型为字符串的配置参数 */
    const ParseConf_Params_str *p_params_str = parseconf_params_array_str;
    while (p_params_str->p_name) {
        if (strcmp(p_params_str->p_name, name) == 0) {
            strcpy(p_params_str->p_value, value);

            return 0;
        }
        p_params_str++;
    }

    const ParseConf_Params_bool *p_params_bool = parseconf_params_array_bool;
    while (p_params_bool->p_name) {
        if (strcmp(p_params_bool->p_name, name) == 0) {
            *p_params_bool->p_value = config_string_to_bool(value);

            return 0;
        }
        p_params_bool++;
    }

    const ParseConf_Params_int *p_params_int = parseconf_params_array_int;
    while (p_params_int->p_name) {
        if (strcmp(p_params_int->p_name, name) == 0) {
            sscanf(value, "%d", p_params_int->p_value);

            return 0;
        }
        p_params_int++;
    }

    const ParseConf_Params_uint *p_params_uint = parseconf_params_array_uint;
    while (p_params_uint->p_name) {
        if (strcmp(p_params_uint->p_name, name) == 0) {
            sscanf(value, "%u", p_params_uint->p_value);

            return 0;
        }
        p_params_uint++;
    }

    return -1;
}

int config_parse_file(const char *filepath)
{
    FILE *fp;
    char s[MAXLINE];
    char text[MAXLINE];

    if ((fp = fopen(filepath, "r")) == NULL) {
        err_sys("config_load_file open error");
    }

    while (fgets(s, sizeof(s), fp) != NULL) {
        if (strlen(s) == 0 || s[0] == '#' || !str_contains_ifnot(s, isspace))
            continue;
        str_trim(s, text, sizeof(text)); /* 两端裁剪，去掉首尾空白字符 */
        parse_line(text);
    }

    fclose(fp);
    return 0;
}

void print_tunableParams()
{
    printf("read tunable params from config file = {\n");
    printf("root_dir = %s\n", tunableParams.root_dir);
    printf("number_of_thread_max = %d\n", tunableParams.number_of_thread_max);
    printf("number_of_client_max = %d\n", tunableParams.number_of_client_max);
    printf("ip = %s\n", config_getIP());
    printf("PASV_mode_enable = %d\n", tunableParams.PASV_mode_enable);
    printf("PORT_mode_enable = %d\n", tunableParams.PORT_mode_enable);
    printf("ctrl_port = %d\n", tunableParams.ctrl_port);
    printf("ctrl_conn_time_out = %d\n", tunableParams.ctrl_conn_time_out);
    printf("data_port_PORT = %d\n", tunableParams.data_port_PORT);
    printf("data_port_PASV_min = %d\n", tunableParams.data_port_PASV_min);
    printf("data_port_PASV_max = %d\n", tunableParams.data_port_PASV_max);
    printf("data_conn_time_out = %d\n", tunableParams.data_conn_time_out);

    printf("} // end tunable params\n");
}

/**
 * 默认从配置文件读取ip地址，如果未设置就获取有效本地网络接口地址
 * @return
 */
const char *config_getIP()
{
    char *ip = tunableParams.ip;
    if (ip == NULL || strlen(ip) == 0) {
        socket_getLocalIPAddr(tunableParams.ip);
    }
    return ip;
}


unsigned int cofig_getCtrlPort()
{
    if (tunableParams.ctrl_port == 0) {
        tunableParams.ctrl_port = CTRL_PORT_DFL;
    }
    return tunableParams.ctrl_port;
}

unsigned int config_getDataPort()
{
    if (tunableParams.data_port_PORT == 0) {
        tunableParams.data_port_PORT = DATA_PORT_DFL;
    }
    return tunableParams.data_port_PORT;
}

int config_getNumberOfClientMax()
{
    return tunableParams.number_of_client_max;
}

int config_getNumberOfThreadMax()
{
    return tunableParams.number_of_thread_max;
}

const char *config_getRootDir()
{
    return tunableParams.root_dir;
}